package com.css.crm.service.impl;

import java.io.Serializable;
import java.util.Hashtable;
import java.util.ResourceBundle;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.css.crm.exception.SystemException;
import com.css.crm.message.SystemErrorMessage;
import com.css.crm.service.LdapService;
import com.css.crm.vo.AbstractUser;

/**
 * Implementation class for LdapService to authenticate users and lookup email
 * addresses
 */
/*
 * Log Date Name Description 0024746 2012 DEC 13 lawrence.ng.c.k Creation
 */
public class LdapServiceImpl implements LdapService, Serializable {

	private static final long serialVersionUID = 1L;

	public final String LDAP_PROFILE = "com.css.crm.config.ldap";
	private static final Logger log = Logger.getLogger(LdapServiceImpl.class);

	private final boolean ENABLED;

	private final String INITIAL_CONTEXT_FACTORY;
	private final String PROVIDER_URL;
	private final String SECURITY_AUTHENTICATION;
	private final String SECURITY_PRINCIPAL_PREFIX;
	private final String SECURITY_PRINCIPAL_SUFFIX;
	private final String SECURITY_PRINCIPAL_PROXY;
	private final String SECURITY_CREDENTIALS_PROXY;

	public LdapServiceImpl() {
		ResourceBundle ldapResourceBundle = ResourceBundle.getBundle(LDAP_PROFILE);
		ENABLED = "true".equalsIgnoreCase(ldapResourceBundle.getString("ENABLED"));
		INITIAL_CONTEXT_FACTORY = ldapResourceBundle.getString("INITIAL_CONTEXT_FACTORY");
		PROVIDER_URL = ldapResourceBundle.getString("PROVIDER_URL");
		SECURITY_AUTHENTICATION = ldapResourceBundle.getString("SECURITY_AUTHENTICATION");
		SECURITY_PRINCIPAL_PREFIX = ldapResourceBundle.getString("SECURITY_PRINCIPAL_PREFIX");
		SECURITY_PRINCIPAL_SUFFIX = ldapResourceBundle.getString("SECURITY_PRINCIPAL_SUFFIX");
		SECURITY_PRINCIPAL_PROXY = ldapResourceBundle.getString("SECURITY_PRINCIPAL_PROXY");
		SECURITY_CREDENTIALS_PROXY = ldapResourceBundle.getString("SECURITY_CREDENTIALS_PROXY");
	}

	/**
	 * Authenticate users
	 * 
	 * @param user
	 *            - AbstractUser
	 * @return true if authentication passed
	 * @throws SystemException
	 */
	/*
	 * Log Date Name Description 0024746 2012 DEC 13 lawrence.ng.c.k Creation
	 */
	public boolean authenticate(AbstractUser user) throws SystemException {
		if (!ENABLED) {

			log.info("LDAP Authenticate Service: Disabled (" + user.getId() + ")");

			if (StringUtils.trimToNull(user.getPassword()) == null) {
				log.info("Password is null");
				SystemException e = new SystemException(SystemErrorMessage.LDAP_AUTHENTICATE_FAILURE);
				throw e;
			}

			return true;
		} else {
			Hashtable<String, String> hashtable = new Hashtable<String, String>();
			hashtable.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
			hashtable.put(Context.PROVIDER_URL, PROVIDER_URL);
			hashtable.put(Context.SECURITY_AUTHENTICATION, SECURITY_AUTHENTICATION);
			hashtable.put(Context.SECURITY_PRINCIPAL, SECURITY_PRINCIPAL_PREFIX + user.getId() + SECURITY_PRINCIPAL_SUFFIX);
			hashtable.put(Context.SECURITY_CREDENTIALS, user.getPassword());
			try {
				InitialDirContext initialDirContext = new InitialDirContext(hashtable);
				hashtable = new Hashtable<String, String>();
				hashtable.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
				hashtable.put(Context.PROVIDER_URL, PROVIDER_URL);
				hashtable.put(Context.SECURITY_AUTHENTICATION, SECURITY_AUTHENTICATION);
				hashtable.put(Context.SECURITY_PRINCIPAL, SECURITY_PRINCIPAL_PROXY);
				hashtable.put(Context.SECURITY_CREDENTIALS, SECURITY_CREDENTIALS_PROXY);
				initialDirContext = new InitialDirContext(hashtable);
				Attributes attrs = initialDirContext.getAttributes(SECURITY_PRINCIPAL_PREFIX + user.getId() + SECURITY_PRINCIPAL_SUFFIX);
				try {
					/*
					 * The properties within this try catch should be found in
					 * LDAP
					 */
					user.setChineseName((String) attrs.get("givenName").get());
					user.setEnglishName((String) attrs.get("cn").get());
					user.setEmployeeNumber((String) attrs.get("employeeNumber").get());
					user.setDepartment((String) attrs.get("ou").get());
					user.setDepartmentCode((String) attrs.get("sn").get());
				} catch (NullPointerException npe) {
					SystemException e = new SystemException(SystemErrorMessage.LDAP_AUTHENTICATE_ERROR, npe);
					log.error(e.getNewSystemMessage() + e.getMessage());
					throw e;
				}
				user.setEmailAddress(emailLookup(user.getId()));
			} catch (AuthenticationException ae) {
				SystemException e = new SystemException(SystemErrorMessage.LDAP_AUTHENTICATE_FAILURE, ae);
				log.error(e.getNewSystemMessage() + e.getMessage());
				throw e;
			} catch (NamingException ne) {
				SystemException e = new SystemException(SystemErrorMessage.LDAP_AUTHENTICATE_ERROR, ne);
				log.error(e.getNewSystemMessage() + e.getMessage());
				throw e;
			}
		}
		log.info("LDAP Authenticate Passed: " + user);
		return true;
	}

	/**
	 * Lookup user's email address using hashtable
	 * 
	 * @param ldap
	 *            - String
	 * @return String that represents email address(null if no email address)
	 * 
	 */
	/*
	 * Log Date Name Description 0024746 2012 DEC 13 lawrence.ng.c.k Creation
	 */
	public String emailLookup(String ldap) {
		Hashtable<String, String> hashtable = new Hashtable<String, String>();
		hashtable.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
		hashtable.put(Context.PROVIDER_URL, PROVIDER_URL);
		hashtable.put(Context.SECURITY_AUTHENTICATION, "none");
		try {
			InitialDirContext initialDirContext = new InitialDirContext(hashtable);
			Attributes attrs = initialDirContext.getAttributes(SECURITY_PRINCIPAL_PREFIX + ldap + SECURITY_PRINCIPAL_SUFFIX);
			Attribute attr = attrs.get("mailLocalAddress");
			return (attr != null ? (String) attr.get() : null);
		} catch (NamingException ne) {
			SystemException e = new SystemException(SystemErrorMessage.LDAP_AUTHENTICATE_ERROR, ne);
			log.error(e.getNewSystemMessage() + e.getMessage());
		}
		return null;
	}

}
